package com.weacar.weixin.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;


/**
 * @author Fong
 */
public final class HttpClientUtils {
    private static final Logger LOG = LoggerFactory.getLogger(HttpClientUtils.class);

    private HttpClient client;
    private String useAgent = "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:18.0) Gecko/20100101 Firefox/18.0";
    private String accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
    private int maxTotal = 100;
    private int defaultMaxPerRoute = 20;
    private int connectionTimeout = 40000;
    private int soTimeout = 900000;
    private boolean handler_redirects = true;
    private static final int RETRY_TIME = 3;
    private static final long SLEEP_INTERVAL = 3 * 1000;

    public HttpClientUtils() {
        PoolingClientConnectionManager manager = new PoolingClientConnectionManager();
        manager.setDefaultMaxPerRoute(defaultMaxPerRoute);
        manager.setMaxTotal(maxTotal);

        client = new DefaultHttpClient(manager);

        client.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT,
                soTimeout);
        client.getParams().setIntParameter(
                CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);
        client.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS,
                handler_redirects);

    }

    private void setHeader(HttpRequestBase method) {
        method.setHeader("User-Agent", useAgent);
        method.setHeader("Accept", accept);
        method.setHeader("Connection", "keep-alive");
    }

    @SuppressWarnings("static-access")
    public String httpDelete(String url) {
        String str = null;
        HttpDelete method = null;
        for (int i = 0; i < RETRY_TIME; i++) {
            try {
                if (i > 0) {
                    Thread.currentThread().sleep(SLEEP_INTERVAL);
                }
                method = new HttpDelete(url);
                setHeader(method);
                HttpResponse response = client.execute(method);
                int responseCode = response.getStatusLine().getStatusCode();
                if (responseCode == HttpStatus.SC_OK) {
                    str = EntityUtils.toString(response.getEntity(), "UTF-8");
                } else {
                    LOG.info("Http code when send HttpGet : " + responseCode
                            + " --> RETRY TIME : " + (i + 1));
                    continue;
                }

                if (StringUtils.isNotBlank(str)) {
                    return str;
                }
            } catch (Exception e) {
                LOG.error("Error occurred when send HttpDelete: " + url, e);
            } finally {
                if (method != null && !method.isAborted()) {
                    method.releaseConnection();
                    method.abort();
                }
            }
        }
        return null;
    }

    /**
     * get page.
     */
    @SuppressWarnings("static-access")
    public String httpGet(String url) {
        String str = new String();
        HttpGet method = null;
        for (int i = 0; i < RETRY_TIME; i++) {
            try {
                if (i > 0) {
                    Thread.currentThread().sleep(SLEEP_INTERVAL);
                }
                method = new HttpGet(url);
                setHeader(method);
                HttpResponse response = client.execute(method);
                int responseCode = response.getStatusLine().getStatusCode();
                if (responseCode == HttpStatus.SC_OK) {
                    str = EntityUtils.toString(response.getEntity(), "UTF-8");
                } else {
                    LOG.info("Http code when send HttpGet : " + responseCode
                            + " --> RETRY TIME : " + (i + 1));
                    continue;
                }

                if (StringUtils.isNotBlank(str)) {
                    return str;
                }
            } catch (Exception e) {
                LOG.error("Error occurred when send HttpGet: " + url, e);
            } finally {
                if (method != null && !method.isAborted()) {
                    method.releaseConnection();
                    method.abort();
                }
            }
        }
        return null;
    }

    @SuppressWarnings("static-access")
    public Optional<File> download(String path, String url) {
        HttpGet method = null;
        for (int i = 0; i < RETRY_TIME; i++) {
            try {
                if (i > 0) {
                    Thread.currentThread().sleep(SLEEP_INTERVAL);
                }
                method = new HttpGet(url);
                setHeader(method);
                HttpResponse response = client.execute(method);
                int responseCode = response.getStatusLine().getStatusCode();
                File storeFile = null;
                if (responseCode == HttpStatus.SC_OK) {
                    storeFile = new File(path);
                    FileOutputStream output = new FileOutputStream(storeFile);
                    InputStream input = response.getEntity().getContent();
                    byte b[] = new byte[1024];
                    int j = 0;
                    while ((j = input.read(b)) != -1) {
                        output.write(b, 0, j);
                    }
                    output.flush();
                    output.close();

                } else {
                    LOG.info("Http code when send HttpGet : " + responseCode
                            + " --> RETRY TIME : " + (i + 1) + " --> " + url);
                    continue;
                }
                return Optional.of(storeFile);
            } catch (Exception e) {
                LOG.error("Error occurred when send HttpGet: " + url, e);
            } finally {
                if (method != null && !method.isAborted()) {
                    method.releaseConnection();
                    method.abort();
                }
            }
        }
        return Optional.empty();
    }

    public String httpPost(String url, Map<String, String> parameter) {
        return httpPost(url, parameter, null);
    }

    @SuppressWarnings("static-access")
    public String httpPost(String url, Map<String, String> parameter,
                           String encodeCharset) {
        String responseBody = null;
        HttpPost post = null;
        for (int i = 0; i < RETRY_TIME; i++) {
            try {
                post = new HttpPost(url);
                if (i > 0) {
                    Thread.currentThread().sleep(SLEEP_INTERVAL);
                }
                List<NameValuePair> formParams = new ArrayList<NameValuePair>();
                for (Map.Entry<String, String> entry : parameter.entrySet()) {
                    formParams.add(new BasicNameValuePair(entry.getKey(), entry
                            .getValue()));
                }

                post.setEntity(new UrlEncodedFormEntity(formParams,
                        encodeCharset == null ? "UTF-8" : encodeCharset));
                HttpResponse response = client.execute(post);
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    HttpEntity entity = response.getEntity();
                    if (entity != null) {
                        responseBody = EntityUtils.toString(
                                response.getEntity(), "UTF-8");
                    }
                } else {
                    LOG.info("Http code when send HttpPost : "
                            + response.getStatusLine().getStatusCode()
                            + " --> RETRY TIME : " + (i + 1));
                    continue;
                }

                if (StringUtils.isNotBlank(responseBody)) {
                    return responseBody;
                }
            } catch (Exception e) {
                LOG.error("Error occurred when send HttpPost: " + url, e);
            } finally {
                if (post != null && !post.isAborted()) {
                    post.abort();
                }
                post.releaseConnection();
            }
        }
        return responseBody;
    }

    public String httpPost(String url, String postData) {
        return httpPost(url, postData, null, null);
    }

    @SuppressWarnings("static-access")
    public String httpPost(String url, String postData, String encodeCharset,
                           String contentType) {
        contentType = contentType == null ? "application/json" : contentType;
        String responseBody = null;
        HttpPost post = null;
        for (int i = 0; i < RETRY_TIME; i++) {

            try {
                if (i > 0) {
                    Thread.currentThread().sleep(SLEEP_INTERVAL);
                }
                post = new HttpPost(url);
                post.setHeader("Content-Type", contentType);
                post.setEntity(new StringEntity(postData,
                        encodeCharset == null ? "UTF-8" : encodeCharset));
                HttpResponse response = client.execute(post);

                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    HttpEntity entity = response.getEntity();
                    if (entity != null) {
                        responseBody = EntityUtils.toString(
                                response.getEntity(), "UTF-8");
                    }
                } else {
                    LOG.info("Http code when send HttpPost : "
                            + response.getStatusLine().getStatusCode()
                            + " --> RETRY TIME : " + (i + 1));
                    continue;
                }

                if (StringUtils.isNotBlank(responseBody)) {
                    return responseBody;
                }
            } catch (Exception e) {
                LOG.error("Error occurred when send HttpPost: " + url, e);
            } finally {
                if (post != null && !post.isAborted()) {
                    post.releaseConnection();
                    post.abort();
                }
            }
        }
        return responseBody;
    }

    public String getUseAgent() {
        return useAgent;
    }

    public void setUseAgent(String useAgent) {
        this.useAgent = useAgent;
    }

    public String getAccept() {
        return accept;
    }

    public void setAccept(String accept) {
        this.accept = accept;
    }

    public int getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
        if (null != client) {
            client.getParams().setIntParameter(
                    CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);
        }
    }

    public int getSoTimeout() {
        return soTimeout;
    }

    public void setSoTimeout(int soTimeout) {
        this.soTimeout = soTimeout;
        if (null != client) {
            client.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT,
                    soTimeout);
        }
    }

    public boolean isHandler_redirects() {
        return handler_redirects;
    }

    public void setHandler_redirects(boolean handler_redirects) {
        this.handler_redirects = handler_redirects;
        if (null != client) {
            client.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS,
                    handler_redirects);
        }
    }



    public HttpClient getClient() {
        return client;
    }
}
